﻿using eslib.nnp5.layers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace eslib.nnp5.ACK
{
    /// <summary>
    /// 通用Ack系统的包类型定义
    /// </summary>
    public enum GenericACKPkgType
    {
        /// <summary>
        /// 数据包
        /// </summary>
        Data = 0,

        /// <summary>
        /// Ack包
        /// </summary>
        Ack = 1
    }



    /// <summary>
    /// 通用ACK系统(独立，无协议依赖性)
    /// 数据包结构： [包类型][数据段]
    /// 包类型:0表示数据包 1表示Ack包
    /// 数据段:当包类型为数据时，数据段就是数据本身。Ack包没有数据段
    /// </summary>
    public abstract class GenericACK
    {
        /// <summary>
        /// 构造,默认1000ms超时
        /// </summary>
        /// <param name="ackTimeoutMs"></param>
        protected GenericACK(int ackTimeoutMs = 1000)
        {
            AckTimeoutMs = ackTimeoutMs;
        }


        #region 超时控制

        /// <summary>
        /// Ack超时时间(ms)
        /// </summary>
        public int AckTimeoutMs { get; set; }


        /// <summary>
        /// 收到Ack为True
        /// </summary>
        protected bool AckRecv
        {
            get
            {
                lock (this)
                {
                    return ackRecv;
                }
            }
            set
            {
                lock (this)
                {
                    ackRecv = value;
                }
            }
        }
        private bool ackRecv = true;


        /// <summary>
        /// 持续等待ack
        /// </summary>
        public void WaitAck()
        {
            while (true)
            {
                if (AckRecv) return;
                else
                {
                    Task.Delay(1).Wait();
                }
            }
        }


        /// <summary>
        /// 等待ack，直到cancel
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public async Task WaitAck(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                if (AckRecv) return;
                else
                {
                    await Task.Delay(1, token);
                }
            }
        }

        /// <summary>
        /// 等待ack，持续ms毫秒
        /// </summary>
        /// <param name="ms"></param>
        /// <returns></returns>
        public void WaitAck(int ms)
        {
            for (int i = 0; i < ms; i++)
            {
                if (AckRecv) return;
                else
                {
                    Task.Delay(1).Wait();
                }
            }
        }


        #endregion



        #region 由具体协议实现

        /// <summary>
        /// 实际数据的发送实现
        /// </summary>
        /// <param name="buffer"></param>
        protected abstract void SendBuffer(byte[] buffer);



        /// <summary>
        /// [异步运行]由具体协议实现的新数据包接收事件调用
        /// </summary>
        /// <param name="buffer"></param>
        protected void RecvBufferEvent(byte[] buffer)
        {
            if (buffer.Length < 1) return;

            switch ((GenericACKPkgType)buffer[0])
            {
                case GenericACKPkgType.Data:
                    //先回发Ack
                    SendAckPkg();

                    //新数据包事件
                    NewPkgEvent?.Invoke(buffer.Skip(1).ToArray());        //去掉包类型1字节                                        
                    break;

                case GenericACKPkgType.Ack:
                    //收到Ack，触发事件
                    AckRecv = true;
                    AckEvent?.Invoke();
                    break;
            }
        }

        #endregion




        /// <summary>
        /// 发送数据包
        /// 如果收到Ack将触发AckEvent事件
        /// 如果超时收不到Ack将触发AckTimeoutEvent事件
        /// </summary>
        /// <param name="pkg"></param>
        public void SendPkg(byte[] pkg)
        {
            //构造数据包
            List<byte> list = new List<byte>(pkg);
            list.Insert(0, (byte)GenericACKPkgType.Data);
            SendBuffer(list.ToArray());
            AckRecv = false;        //重置Ack状态

            //计时，超时无Ack引发事件
            Task.Run(async () =>
              {
                  await Task.Delay(AckTimeoutMs);
                  if (!AckRecv)     //未收到Ack
                  {
                      AckTimeoutEvent?.Invoke();
                  }
              });
        }


        /// <summary>
        /// 发送Ack包
        /// </summary>
        protected void SendAckPkg()
        {
            SendBuffer(new byte[] { (byte)GenericACKPkgType.Ack });
        }



        /// <summary>
        /// 收到新数据包时引发
        /// </summary>
        public event NewTransportLayerDataEventHandler NewPkgEvent;

        /// <summary>
        /// 收到Ack时引发
        /// </summary>
        public event GenericAckEventDelegate AckEvent;

        /// <summary>
        /// 超时没有收到Ack时引发
        /// </summary>
        public event GenericAckEventDelegate AckTimeoutEvent;
    }



    /// <summary>
    /// ack事件委托
    /// </summary>
    public delegate void GenericAckEventDelegate();

}
